#!/usr/bin/env node

const fs = require('fs');
const program = require('commander');
const logSymbols = require('log-symbols');
const chalk = require('chalk');
const AdmZip = require("adm-zip");
const { exec } = require('child_process');
const ora = require('ora');


// 简写console.log
const log = console.log;

program
    .version('1.0.0', '-v, --version', '查看版本号')
    .helpOption('-h, --help', '查看帮助信息');

program
    .command('build <resources> <apk> <path> <keystore> <password>')
    .description('根据uniapp生成的本地android打包资源 + 母包apk，构建新的apk')
    .action(async function (resources, apk, path, keystore, password) {
        // loading加载
        const spinner = ora('开始打包').start();
        try {
            // 资源路径检查
            if (!fs.existsSync(resources)) throw '本地android打包资源不存在 ' + resources;
            // 母包路径检查
            if (!fs.existsSync(apk)) throw '母包apk不存在 ' + apk;
            // 打包路径检查
            if (!fs.existsSync(splitPath(path).dir)) throw '打包输出path不存在 ' + path;
            // 证书路径检查
            if (!fs.existsSync(keystore)) throw '签名证书不存在 ' + keystore;
            // 证书密码检查
            if (!password) throw '请输入签名证书密码';

            // log('[构建apk]', resources, apk, path, keystore, password);

            // 复制母包生成备用apk
            await copyFile(apk, path);

            // 移除签名
            deleteSigner(path);

            // 替换www资源
            replaceApkDir(path, '/assets/apps/', resources);

            // 签名apk
            await createSigner(path, keystore, password);

            // loading结束
            spinner.stop();
            log(logSymbols.success, chalk.green('打包成功 ' + path));
        } catch (error) {
            if (fs.existsSync(path)) fs.unlinkSync(path);
            // loading结束
            spinner.stop();
            log(logSymbols.error, chalk.red(error));
        }
    }).on('--help', function () {
        log('');
        log('uniapp-build build 本地android打包资源 apk母包 新apk keystore证书 证书密码');
        log('例如: uniapp-build build E:/fubao/FuBao/unpackage/resources/ E:/fubao/FuBao/unpackage/release/apk/app.apk E:/fubao/FuBao/unpackage/newApp.apk E:/fubao/FuBao/taofubao.keystore password');
        // uniapp-build build E:/fubao/FuBao/unpackage/resources/ E:/fubao/FuBao/unpackage/release/apk/app.apk E:/fubao/FuBao/unpackage/newApp.apk E:/fubao/FuBao/taofubao.keystore moguiqiang66
    });


program.parse(process.argv);


/**
 * 复制文件
 * @param {string} file 复制文件
 * @param {string} path 输出目录
 * @returns Promise
 */
function copyFile(file, path) {
    return new Promise((resolve, reject) => {
        try {
            const rs = fs.createReadStream(file);
            const ws = fs.createWriteStream(path);
            rs.on('data', (chunk) => {
                ws.write(chunk);
            });
            rs.on('end', () => {
                resolve()
                ws.end();
            })
        } catch (err) {
            reject(err)
        }
    })
}

/**
 * 替换apk中的目录
 * @param {string} apk 包文件
 * @param {string} apkPath 包替换目录
 * @param {string} path 文件夹路径
 */
function replaceApkDir(apk, apkPath, path) {
    const zip = AdmZip(apk);
    zip.addLocalFolder(path, apkPath);
    zip.writeZip();
}

/**
 * 签名apk
 * @param {string} apk 包文件
 * @param {string} keystore 证书
 * @param {string} password 证书密码
 * @returns Promise
 */
function createSigner(apk, keystore, password) {
    return new Promise((resolve, reject) => {
        const cmd = `apksigner sign --ks ${keystore} --ks-pass pass:${password} ${apk}`
        exec(cmd, (error) => {
            if (error) reject(error);
            else resolve();
        })
    })
}

/**
 * 拆分文件路径
 * @param {string} path 文件路径字符串
 * @returns 相关目录、文件名
 */
function splitPath(path) {
    const fileName = path.split('/').pop();
    const fileDir = path.slice(0, -fileName.length) || './';
    return {
        dir: fileDir,
        name: fileName
    }
}

/**
 * 移除apk签名
 * @param {string} apk 包文件
 * @returns {Promise}
 */
function deleteSigner(apk) {
    // 移除META-INF签名目录
    const zip = new AdmZip(apk);
    zip.deleteFile("META-INF/MANIFEST.MF");
    zip.deleteFile("META-INF/TAOFUBAO.RSA");
    zip.deleteFile("META-INF/TAOFUBAO.SF");
    zip.writeZip();
}
